/**
 * Radio Story Cards - using RFID tags to track physical story cards in an agile/lean environment.
 *
 * See http://www.radiostorycards.com for more information.
 *
 *
 * Copyright (C) 2011, Ted M. Young
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.radiostorycards.persistence;

import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;
import com.google.common.annotations.VisibleForTesting;
import com.radiostorycards.domain.cards.RadioCard;
import com.radiostorycards.domain.cards.RadioCardDao;
import com.radiostorycards.domain.tags.RfidTag;

import java.io.*;
import java.util.List;
import java.util.Map;

import static com.google.common.collect.Maps.newHashMap;

/**
 * This is the persistence store for card data. Reads and writes to a CSV file.
 *
 * @author Ted Young <tedyoung@gmail.com> @JitterTed
 */
public class RadioCardStore {

  private Map<RfidTag, RadioCard> _radioCards = newHashMap();

  public static RadioCardStore newStore() {
    return new RadioCardStore();
  }

  /**
   * Loads the store from the specified file if it exists, otherwise returns an empty store.
   * @param storeName name of the store to load from
   * @return the loaded store if it exists, otherwise an empty store.
   */
  public static RadioCardStore loadStoreFrom(String storeName) {
    RadioCardStore store = newStore();
    String storeFileName = createStoreName(storeName);
    File storeFile = new File(storeFileName);
    if (storeFile.exists()) {
      FileReader fileReader = null;
      try {
        fileReader = new FileReader(storeFileName);
        List<String[]> rows = readRowsFrom(fileReader);
        store.loadStoreFrom(rows);
      } catch (FileNotFoundException e) {
        throw new IllegalStateException("We checked that the file exists, so this error shouldn't occur.");
      } catch (IOException e) {
        throw new RuntimeException("Exception while trying to load data from file", e);
      } finally {
        if (fileReader != null) {
          try {
            fileReader.close();
          } catch (IOException e) {
            throw new RuntimeException("Exception while trying to close FileReader.", e);
          }
        }
      }
    }
    return store;
  }

  static List<String[]> readRowsFrom(FileReader fileReader) throws IOException {
    CSVReader csvReader = new CSVReader(fileReader);
    return csvReader.readAll();
  }

  @VisibleForTesting
  void loadStoreFrom(List<String[]> rows) {
    for (String[] cells : rows) {
      RadioCard radioCard = RadioCardDao.fromRowOfCells(cells);
      _radioCards.put(radioCard.getRfidTag(), radioCard);
    }
  }

  public void saveStoreTo(String storeName) throws IOException {
    CSVWriter writer = new CSVWriter(new FileWriter(createStoreName(storeName)));
    for (RadioCard radioCard : _radioCards.values()) {
      writer.writeNext(RadioCardDao.toRowOfCells(radioCard));
    }
    writer.close();
  }

  @VisibleForTesting
  static String createStoreName(String storeName) {
    return storeName + ".csv";
  }

  public void add(RadioCard radioCard) {
    _radioCards.put(radioCard.getRfidTag(), radioCard);
  }

  public RadioCard findByTag(RfidTag rfidTag) {
    return _radioCards.get(rfidTag);
  }

  public int size() {
    return _radioCards.size();
  }
}
